﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace Pavilion
{
    public class DrawManager
    {
        static private DrawManager singletonInstance;

        private SpriteBatch SpriteBatch { get; set; }
        private Texture2D texture;
        private List<DrawItem> drawItemList;

        public Vector2 FocusStartingPoint;
        public bool IsDrawing { get; private set; }
        public GraphicsDevice Graphics { get; private set; }
       
        public SpriteFont GameFont { get; set; }

        public DrawManager()
        {
            singletonInstance = this;

            drawItemList = new List<DrawItem>();
            FocusStartingPoint = new Vector2();
        }

        public void Initialize(GraphicsDevice graphics)
        {
            this.Graphics = graphics;
            SpriteBatch = new SpriteBatch(Graphics);

            texture = CTexture2D.CreateTexture(1, 1);
            texture.SetData<Color>(new Color[] { Color.White });
        }

        /// <summary>
        /// Gets the single instance of this class.
        /// </summary>
        static public DrawManager Instance
        {
            get
            {
                if (singletonInstance == null)
                    new DrawManager();

                return singletonInstance;
            }
        }

        public Vector2 MeasureString(string text)
        {
            return GameFont.MeasureString(text);
        }

        public void Draw()
        {
            foreach (DrawItem drawItem in drawItemList)
                drawItem.DrawAction.Invoke();
        }

        public void Clear()
        {
            for (int i = 0; i < drawItemList.Count; )
                drawItemList[i].Dispose();
        }

        public void Add(DrawItem drawItem)
        {
            if (drawItemList.Contains(drawItem))
                return;

            for (int i = 0; i < drawItemList.Count; i++)
            {
                if (drawItemList[i].DrawOrder > drawItem.DrawOrder)
                {
                    drawItemList.Insert(i, drawItem);
                    drawItem.DrawOrderChanged += new Action<DrawItem>(DrawItem_DrawOrderChanged);
                    drawItem.Disposed += new Action<DrawItem>(DrawItem_Disposed);
                    return;
                }
            }

            drawItemList.Add(drawItem);
        }

        private void DrawItem_Disposed(DrawItem drawItem)
        {
            Remove(drawItem);
        }

        public void Remove(DrawItem drawItem)
        {
            drawItemList.Remove(drawItem);
            drawItem.DrawOrderChanged -= new Action<DrawItem>(DrawItem_DrawOrderChanged);
            drawItem.Disposed -= new Action<DrawItem>(DrawItem_Disposed);
        }
        
        private Vector2 CalculateDrawingPosition(Vector2 position)
        {
            position.X = position.X - FocusStartingPoint.X;
            position.Y = position.Y - FocusStartingPoint.Y;

            return position;
        }

        private Rectangle CalculateDrawingPosition(Rectangle positionRectangle)
        {
            positionRectangle.X = positionRectangle.X - (int)FocusStartingPoint.X;
            positionRectangle.Y = positionRectangle.Y - (int)FocusStartingPoint.Y;

            return positionRectangle;
        }

        private void DrawItem_DrawOrderChanged(DrawItem drawItem)
        {
            Remove(drawItem);
            Add(drawItem);
        }

        public void Begin(SpriteSortMode sortMode, BlendState blendState)
        {
            IsDrawing = true;
            SpriteBatch.Begin(sortMode, blendState);
        }

        public void End()
        {
            SpriteBatch.End();
            IsDrawing = false;
        }

        #region Draw Methods

        public void DrawTextOnScreen(string text, Vector2 position, Color color, float scale, bool positionAtOrigin)
        {
            if (positionAtOrigin)
                SpriteBatch.DrawString(GameFont, text, position, color, 0f, GameFont.MeasureString(text) / 2, scale, SpriteEffects.None, 0f);
            else
                SpriteBatch.DrawString(GameFont, text, position, color, 0f, default(Vector2), scale, SpriteEffects.None, 0f);
        }

        public void DrawLine(Vector2 start, Vector2 end, Color color)
        {
            this.DrawLine(start, end, default(Vector2), color);
        }

        public void DrawLine(Vector2 start, Vector2 end, Vector2 origin, Color color)
        {
            int distance = (int)Vector2.Distance(start, end);
            float alpha = (float)Math.Atan2(end.Y - start.Y, end.X - start.X);
            Rectangle calculatedRectangle = CalculateDrawingPosition(new Rectangle((int)(start.X - origin.X), (int)(start.Y - origin.Y), distance, 1));

            SpriteBatch.Draw(texture, calculatedRectangle,
                    null, color, alpha, default(Vector2), SpriteEffects.None, 0);
        }

        public void DrawLineOnScreen(Vector2 start, Vector2 end, Color color)
        {
            int distance = (int)Vector2.Distance(start, end);
            float alpha = (float)Math.Atan2(end.Y - start.Y, end.X - start.X);

            SpriteBatch.Draw(texture, new Rectangle((int)start.X, (int)start.Y, distance, 1),
                    null, color, alpha, default(Vector2), SpriteEffects.None, 0);
        }

        public void DrawPolygonBox(PolygonBox polygonBox, Color color)
        {
            if (polygonBox.Count > 1)
            {
                Vector2 previousPoint = polygonBox[0];

                for (int i = 1; i < polygonBox.Count; i++)
                {
                    DrawManager.Instance.DrawLine(previousPoint, polygonBox[i], color);
                    previousPoint = polygonBox[i];
                }

                DrawManager.Instance.DrawLine(previousPoint, polygonBox[0], color);
            }
        }

        public void DrawRectangle(Rectangle rectangle, Color color)
        {
            DrawLine(new Vector2(rectangle.Left, rectangle.Top), new Vector2(rectangle.Right, rectangle.Top), color);
            DrawLine(new Vector2(rectangle.Left, rectangle.Top), new Vector2(rectangle.Left, rectangle.Bottom), color);
            DrawLine(new Vector2(rectangle.Left, rectangle.Bottom), new Vector2(rectangle.Right, rectangle.Bottom), color);
            DrawLine(new Vector2(rectangle.Right, rectangle.Top), new Vector2(rectangle.Right, rectangle.Bottom), color);
        }

        public void DrawRectangleOnScreen(Rectangle rectangle, Color color)
        {
            DrawLineOnScreen(new Vector2(rectangle.Left, rectangle.Top), new Vector2(rectangle.Right, rectangle.Top), color);
            DrawLineOnScreen(new Vector2(rectangle.Left, rectangle.Top), new Vector2(rectangle.Left, rectangle.Bottom), color);
            DrawLineOnScreen(new Vector2(rectangle.Left, rectangle.Bottom), new Vector2(rectangle.Right, rectangle.Bottom), color);
            DrawLineOnScreen(new Vector2(rectangle.Right, rectangle.Top), new Vector2(rectangle.Right, rectangle.Bottom), color);
        }

        public void FillRectangleOnScreen(Rectangle rectangle, Color color)
        {
            SpriteBatch.Draw(texture, rectangle, color);
        }

        public void DrawPointOnScreen(Vector2 position, Color color)
        {
            SpriteBatch.Draw(texture, position, color);
        }

        public void Draw(Texture2D texture, Vector2 position, Color color)
        {
            SpriteBatch.Draw(texture, CalculateDrawingPosition(position), color);
        }

        public void Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color)
        {
            SpriteBatch.Draw(texture, CalculateDrawingPosition(position), sourceRectangle, color);
        }

        public void Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth)
        {
            SpriteBatch.Draw(texture, CalculateDrawingPosition(position), sourceRectangle, color, rotation, origin, scale, effects, layerDepth);
        }

        public void Draw(Texture2D texture, Rectangle destinationRectangle, Color color)
        {
            SpriteBatch.Draw(texture, CalculateDrawingPosition(destinationRectangle), color);
        }

        public void DrawOnScreen(Texture2D texture, Vector2 position, Color color)
        {
            SpriteBatch.Draw(texture, position, color);
        }

        public void DrawOnScreen(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color)
        {
            SpriteBatch.Draw(texture, position, sourceRectangle, color);
        }

        public void DrawOnScreen(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth)
        {
            SpriteBatch.Draw(texture, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth);
        }

        public void DrawOnScreen(Texture2D texture, Rectangle destinationRectangle, Color color)
        {
            SpriteBatch.Draw(texture, destinationRectangle, color);
        }

        #endregion
    }
}
